﻿using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace DotNetty.Wraper
{
    class TcpSocketServerBuilder :
        BaseGenericServerBuilder<ITcpSocketServerBuilder, ITcpSocketServer, ITcpSocketConnection, byte[]>,
        ITcpSocketServerBuilder
    {
        public TcpSocketServerBuilder(int port)
            : base(port)
        {
            //this.AddLast(new Handlers.Logging.LoggingHandler());
        }

        protected Action<IChannelPipeline> _setEncoder { get; set; }

        public ITcpSocketServerBuilder SetLengthFieldDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, ByteOrder byteOrder = ByteOrder.BigEndian)
        {
            AddLast(new LengthFieldBasedFrameDecoder(byteOrder, maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, true));

            return this;
        }

        public ITcpSocketServerBuilder SetLengthFieldEncoder(int lengthFieldLength)
        {
            AddLast(new LengthFieldPrepender(lengthFieldLength));

            return this;
        }
        /// <summary>
        /// Inserts multiple <see cref="IChannelHandler"/>s at the last position of this pipeline.
        /// </summary>
        /// <param name="handlers"></param>
        /// <returns></returns>
        public ITcpSocketServerBuilder AddLast(params IChannelHandler[] handlers)
        {
            _setEncoder += x => x.AddLast(handlers);

            return this;
        }
        /// <summary>
        /// Appends an <see cref="Transport.Channels.IChannelHandler"/> at the last position of this pipeline.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="handler"></param>
        /// <returns></returns>
        public ITcpSocketServerBuilder AddLast(string name, IChannelHandler handler)
        {
            _setEncoder += x => x.AddLast(name, handler);

            return this;
        }

        public async override Task<ITcpSocketServer> BuildAsync(Func<ITcpSocketServer, IChannelHandler> addChannelHandler = null, Action<Transport.Channels.IChannelPipeline> OnPipelineAction = null
             , bool useLibuv = false, System.Security.Cryptography.X509Certificates.X509Certificate2 tlsCertificate = null)
        {
            return await BuildAsync(addChannelHandler, OnPipelineAction, useLibuv, tlsCertificate, 100, 1);
        }

        public async Task<ITcpSocketServer> BuildAsync(Func<ITcpSocketServer, IChannelHandler> addChannelHandler = null, System.Action<Transport.Channels.IChannelPipeline> OnPipelineAction = null
             , bool useLibuv = false, System.Security.Cryptography.X509Certificates.X509Certificate2 tlsCertificate = null, int SoBacklog = 8192, int eventLoopCount = 1)
        {
            TcpSocketServer tcpServer = new TcpSocketServer(_port, _event);
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;
            }
            IEventLoopGroup bossGroup;
            IEventLoopGroup workerGroup;//Default eventLoopCount is Environment.ProcessorCount * 2

            var bootstrap = new ServerBootstrap();
            if (useLibuv)
            {
                var dispatcher = new Transport.Libuv.DispatcherEventLoopGroup();
                bossGroup = dispatcher;
                workerGroup = new Transport.Libuv.WorkerEventLoopGroup(dispatcher);

                bootstrap.Group(bossGroup, workerGroup);
                bootstrap.Channel<Transport.Libuv.TcpServerChannel>();

                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
                       || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                {
                    bootstrap
                        .Option(ChannelOption.SoReuseport, true)
                        .ChildOption(ChannelOption.SoReuseaddr, true);
                }
            }
            else
            {
                bossGroup = new MultithreadEventLoopGroup(eventLoopCount);
                workerGroup = new MultithreadEventLoopGroup();

                bootstrap.Group(bossGroup, workerGroup);
                bootstrap.Channel<TcpServerSocketChannel>();
            }
            var bootstrapChannel = await bootstrap
            .Option(ChannelOption.SoBacklog, SoBacklog)
            .Handler(new Handlers.Logging.LoggingHandler("SRV-LSTN"))
            //.ChildOption(ChannelOption.Allocator, PooledByteBufferAllocator.Default)
            //.Group(bossGroup, workerGroup)
            .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
            {
                var pipeline = channel.Pipeline;
                if (tlsCertificate != null)
                {
                    pipeline.AddLast(Handlers.Tls.TlsHandler.Server(tlsCertificate));
                }
                _setEncoder?.Invoke(pipeline);

                OnPipelineAction?.Invoke(pipeline);

                if (addChannelHandler != null)
                    pipeline.AddLast("tcpServer", addChannelHandler(tcpServer));
                else
                    pipeline.AddLast("tcpServer", new CommonChannelHandler(tcpServer));
            })).BindAsync(_port);

            _event.OnServerStarted?.Invoke(tcpServer);

            tcpServer.SetChannel(bootstrapChannel);
            tcpServer.AfterClose = () =>
            {
                Task.WhenAll(
                   bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)),
                   workerGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1))).Wait();
            };
            return await Task.FromResult(tcpServer);
        }
    }
}